package eitwitter.learning.models;

import eitwitter.learning.CategoryInformations;
import eitwitter.learning.WordInformations;
import eitwitter.normalization.Normalizer;
import eitwitter.storage.Tweet;
import eitwitter.storage.Vocabulary;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

/**
 * Implementation du modele binomial
 * @author AH & PJ
 */
public class BinomialModel implements ClassificationModel {

	/** Parametre du modele binomial */
	private double k;

	public BinomialModel(double k) {
		this.k = k;
	}
	
	/**
	 * Calcule les betas de la categorie suivant le modele binomial
	 * @param categoryInformations	Categorie dont on veut calculer les betas
	 */
	@Override
	public void computeBetas(CategoryInformations categoryInformations) {
		
		// Nombre de tweets de la categorie
		double tweetsNumber = categoryInformations.getTweetsNumber();
		
		// Pour tous les mots de la categorie
		for(Map.Entry<Integer, WordInformations> word: categoryInformations.getWords().entrySet()){
			
			// Nombre de documents de la categorie ou apparait le mot
			double docsNumber = word.getValue().getDocumentsNumber();
			
			// Calcul du beta du mot
			word.getValue().setBeta(docsNumber/tweetsNumber);
		}
	}

	@Override
	public double computeInference(Tweet tweet, CategoryInformations categoryInformations) {
		
		// Decoupage du tweet selon le Normalizer de la categorie
		Normalizer normalizer = categoryInformations.getNormalizer();
		HashMap<String, Integer> tweetWords = normalizer.normalize(tweet.getContent());
		
		// Reference vers le vocabulaire
		Vocabulary vocabulary = categoryInformations.getVocabulary();
		
		// On recupere les id des mots du tweet
		ArrayList<Integer> tweetWordsIds = new ArrayList<Integer>();
		for(String word : tweetWords.keySet()){
			int id = vocabulary.getId(word);
			
			// Si le mot est connu
			if(id != -1){
				
				// On l'ajoute
				tweetWordsIds.add(id);
			}
		}
		
		double inference = 0.0;
		double beta;
				
		// Parcours de tous les mots de la categorie
		for(Map.Entry<Integer, WordInformations> word : categoryInformations.getWords().entrySet()){
			beta = word.getValue().getBeta();
			
			// Si le mot courant est contenu dans le tweet
			if(tweetWordsIds.contains(word.getKey())){
				inference += Math.log(beta);
				
				// On le supprime de la liste a analyser
				tweetWordsIds.remove(word.getKey());
			} else {
				inference += Math.log(1. - beta);
			}
		}
		
		// Pour tous les mots qui ne sont pas presents dans la categorie le beta est le meme
		beta = k / categoryInformations.getTweetsNumber();
		
		// Nombre de mots du tweet appartenant a d'autres categories
		double leftWordsNumber = tweetWordsIds.size();
		inference += leftWordsNumber * Math.log(beta);
		
		// Nombre de mots du dictionnaire non presents dans le tweet ni dans la categorie
		leftWordsNumber = vocabulary.size() - categoryInformations.getWords().size() - tweetWordsIds.size();
		inference += leftWordsNumber * Math.log(1. - beta);
		
		return inference;
	}
	
}
